using System;
using System.IO;
using System.Text;

namespace Needle.Utils {

    /// <summary>
    /// Filesystem common folders and utilities
    /// </summary>
    public static class FileSystem {

        private static StringComparison pathStringComparison;
       
        static FileSystem() {
            if (PathsAreCaseSensitive) {
                pathStringComparison = StringComparison.Ordinal;
            } else {
                pathStringComparison = StringComparison.OrdinalIgnoreCase;
            }
        }

        /// <summary>
        /// True if on unix, false otherwise.
        /// </summary>
        public static bool PathsAreCaseSensitive {
            get { return Environment.OSVersion.Platform == PlatformID.Unix; }
        }
        /// <summary>
        /// The application data folder that is shared between users.
        /// </summary>
        public static DirectoryInfo CommonAppDataFolder {
            get { return new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); }
        }
        /// <summary>
        /// The local (non-roaming) user application data folder.
        /// </summary>
        public static DirectoryInfo LocalAppDataFolder {
            get { return new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)); }
        }
        /// <summary>
        /// The roaming user application data folder.
        /// </summary>
        public static DirectoryInfo AppDataFolder {
            get { return new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); }
        }
       
        /// <summary>
        /// The system folder.
        /// </summary>
        public static DirectoryInfo SystemFolder {
            get { return new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.System)); }
        }
       
        /// <summary>
        /// The temporary files folder.
        /// </summary>
        public static DirectoryInfo TempFolder {
            get { return new DirectoryInfo(Path.GetTempPath()); }
        }
        /// <summary>
        /// Creates and returns a new temporary file.
        /// </summary>
        public static FileInfo GetNewTempFile() {
            return new FileInfo(Path.GetTempFileName());
        }
        /// <summary>
        /// Returns the common base path of the arguments or null if there is no common path.
        /// </summary>
        public static DirectoryInfo GetCommonBaseFolder(FileSystemInfo a, FileSystemInfo b) {
            Ensure.NotNull(a, "a");
            Ensure.NotNull(b, "b");

            string[] partsA = a.FullName.Split(new char[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
            string[] partsB = b.FullName.Split(new char[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
            int lastCommonPart = -1;

            for (int i = 0 ; i < Math.Min(partsA.Length, partsB.Length) ; i++) {
                if (String.Compare(partsA[i], partsB[i], pathStringComparison) == 0) {
                    lastCommonPart = i;
                } else {
                    break;
                }
            }
            if (lastCommonPart < 0)
                return null;
            string baseFolder = String.Join(Path.DirectorySeparatorChar.ToString(), partsA, 0, lastCommonPart + 1) + Path.DirectorySeparatorChar;
            return new DirectoryInfo(baseFolder);
        }
        /// <summary>
        /// Returns the relative path to get from basefolder to item, throws if the paths are on different drives.
        /// </summary>
        public static string MakePathRelativeTo(DirectoryInfo baseFolder, FileSystemInfo item) {
            Ensure.NotNull(baseFolder, "baseFolder");
            Ensure.NotNull(item, "item");
            DirectoryInfo myBaseFolder = EnsureTrailingSeparator(baseFolder);
            DirectoryInfo commonBaseFolder = GetCommonBaseFolder(myBaseFolder, item);
            if (commonBaseFolder == null) {
                throw new ArgumentException("There is no common base folder for " + myBaseFolder.FullName + " and " + item.FullName);
            }
            string baseFolderTail = myBaseFolder.FullName.Replace(commonBaseFolder.FullName, "");
            if (String.IsNullOrEmpty(baseFolderTail)) {
                return item.FullName.Replace(commonBaseFolder.FullName, "");
            } else {
                string[] tailParts = baseFolderTail.Split(new char[]{Path.DirectorySeparatorChar}, StringSplitOptions.RemoveEmptyEntries);
                StringBuilder doubleDots = new StringBuilder();
                for (int i = 0 ; i < tailParts.Length ; i++) {
                    doubleDots.Append("..");
                    doubleDots.Append(Path.DirectorySeparatorChar);
                }
                doubleDots.Append(item.FullName.Replace(commonBaseFolder.FullName, ""));
                return doubleDots.ToString();
            }

        }

        /// <summary>
        /// Ensures the folder path has a trailing path separator
        /// </summary>
        public static DirectoryInfo EnsureTrailingSeparator(DirectoryInfo folder) {
            Ensure.NotNull(folder, "folder");
            if (folder.FullName.EndsWith(Path.DirectorySeparatorChar.ToString())) {
                return folder;
            }
            return new DirectoryInfo(folder.FullName + Path.DirectorySeparatorChar);
        }
        /// <summary>
        /// replaces invalid path characters in the string with the given replacement char
        /// </summary>
        public static string ReplaceInvalidPathChars(string path, char replacement) {
            Ensure.NotNull(path,"path");
            if (path.Length < 1)
                return path;
            StringBuilder sb = new StringBuilder(path);
            char[] invalid = Path.GetInvalidPathChars();
            foreach (char c in invalid) {
                if (c == replacement) {
                    throw new ArgumentException("the replacement char is invalid");
                }
            }
            for (int i = 0 ; i < sb.Length ; i++) {
                foreach (char c in invalid) {
                    if (sb[i] == c) {
                        sb[i] = replacement;
                        break;
                    }

                }
            }
            return sb.ToString();
        }

        /// <summary>
        /// replaces invalid filename characters in the string with the given replacement char
        /// </summary>
        public static string ReplaceInvalidFilenameChars(string filename, char replacement) {
            Ensure.NotNull(filename, "filename");
            if (filename.Length < 1)
                return filename;
            StringBuilder sb = new StringBuilder(filename);
            char[] invalid = Path.GetInvalidFileNameChars();
            foreach (char c in invalid) {
                if (c == replacement) {
                    throw new ArgumentException("the replacement char is invalid");
                }
            }
            for (int i = 0 ; i < sb.Length ; i++) {
                foreach (char c in invalid) {
                    if (sb[i] == c) {
                        sb[i] = replacement;
                        break;
                    }

                }
            }
            return sb.ToString();
        }

    }

}
